home *** CD-ROM | disk | FTP | other *** search
/ PD Collection CD 1 / PD Collection CD 1.iso / programer2 / euclidlib / c / cache next >
Text File  |  1992-04-18  |  10KB  |  372 lines

  1. /**** cache.c ****/
  2. /* By Paul Field
  3.  * See !ReadMe file for distribution/modification restrictions
  4.  */
  5.  
  6. #include "cache.h"
  7. #include <assert.h>
  8. #include <stdlib.h>
  9.  
  10. /* Turn on/off cache state change messages */
  11. #define CACHE_DEBUG FALSE
  12.  
  13. /* I may move the list and queue routines into their own modules */
  14. /* It's just that they should be in some sort of data type library */
  15. /* rather than the euclid library */
  16.  
  17. /***** Simple queue routines *****/
  18.  
  19. typedef struct item item;
  20. struct item
  21.  { item *next;
  22.  };
  23.  
  24. typedef struct queue
  25.  { item *front;
  26.    item *rear;
  27.  }queue;
  28.  
  29. static void queue_empty(queue *q)
  30.  { q->front = NULL;
  31.  }
  32.  
  33. static BOOL queue_isempty(queue *q)
  34.  { return(q->front == NULL);
  35.  }
  36.  
  37. static void queue_addvalue(queue *q, item *toadd)
  38.  { if (queue_isempty(q))
  39.     { q->front = toadd;
  40.     }
  41.    else
  42.     { q->rear->next = toadd;
  43.     }
  44.    q->rear = toadd;
  45.    toadd->next = NULL;
  46.  }
  47.  
  48. static item *queue_removevalue(queue *q)
  49.  { item *removed;
  50.  
  51.    assert(!queue_isempty(q));
  52.    removed = q->front;
  53.    q->front = removed->next;
  54.    return(removed);
  55.  }
  56.  
  57. static item *queue_findandremovevalue(queue *q, BOOL (*match)(item*, void*),void *handle)
  58.  { item **itempp;
  59.  
  60.    for (itempp = &q->front; *itempp != NULL; itempp = &(*itempp)->next)
  61.     { if (match(*itempp, handle))
  62.        { item *removed;
  63.  
  64.          removed = *itempp;
  65.          *itempp = removed->next;
  66.          return(removed);
  67.        }
  68.     }
  69.    return(NULL);
  70.  }
  71.  
  72. /***** Simple list routines *****/
  73.  
  74. typedef item *list;
  75.  
  76. static list list_empty(void)
  77.  { return(NULL);
  78.  }
  79.  
  80. static BOOL list_isempty(list l)
  81.  { return(l == NULL);
  82.  }
  83.  
  84. static item *list_head(list l)
  85.  { return(l);
  86.  }
  87.  
  88. static list list_tail(list l)
  89.  { return(l->next);
  90.  }
  91.  
  92. static void list_cons(list *l, item *tocons)
  93.  { tocons->next = *l;
  94.    *l = tocons;
  95.  }
  96.  
  97.  
  98. static void list_remove2(list l, item *toremove, list *last)
  99.  { if (!list_isempty(l))
  100.     { if (list_head(l) == toremove)
  101.        { *last = list_tail(l);
  102.        }
  103.       else
  104.        { list_remove2(list_tail(l), toremove, &list_head(l)->next);
  105.        }
  106.     }
  107.  }
  108.  
  109. static void list_remove(list *l, item *toremove)
  110.  { list_remove2(*l, toremove, l);
  111.  }
  112.  
  113.  
  114. /***** The cache routines *****/
  115.  
  116. #if CACHE_DEBUG
  117.  static void cache_cwfreport(void *cacheaddr, BOOL active);
  118.  static void cache_cfreport(void *cacheaddr, BOOL active);
  119.  static void cache_crpreport(void *cacheaddr, cache_processstart, void *handle);
  120.  static void cache_crapreport(void *cacheaddr, void *handle);
  121. #else
  122. # define cache_cwfreport(ca,a)
  123. # define cache_cfreport(ca,a)
  124. # define cache_crpreport(ca,p,h)
  125. # define cache_crapreport(ca,h)
  126. #endif
  127.  
  128.  
  129. typedef struct pendingprocess
  130.  { void *next;
  131.    cache_processstart fn;
  132.    void *handle;
  133.  }pendingprocess;
  134.  
  135. typedef struct cache
  136.  { void  *next;
  137.    void  *cacheaddr;
  138.    queue pendingprocesses;
  139.  }cache;
  140.  
  141. static list activecaches = NULL;
  142.     /* List of active caches with pending processes. */
  143.     /* A binary tree or hash table might be faster but   */
  144.     /* I can't see applications using more than a few    */
  145.     /* caches at one time - so lists will be fast enough */
  146.  
  147.  
  148. /**** Goodness Me. First abstract data types and now recursion.        ****/
  149. /**** With all this good programming practice, it might actually work. ****/
  150.  
  151. static cache *cache_find2(list remaining, void *cacheaddr)
  152.  { if (list_isempty(remaining))
  153.     { return(NULL);
  154.     }
  155.    else
  156.     { cache *head;
  157.  
  158.       head = (cache *)list_head(remaining);
  159.       if (head->cacheaddr == cacheaddr)
  160.        { return(head);
  161.        }
  162.       else
  163.        { return(cache_find2(list_tail(remaining), cacheaddr));
  164.        }
  165.     }
  166.  }
  167.  
  168. static cache *cache_find(void *cacheaddr)
  169.  { return(cache_find2(activecaches, cacheaddr));
  170.  }
  171.  
  172.  
  173.  
  174. BOOL cache_callwhenfree(void *cacheaddr, cache_processstart fn, void *handle)
  175.  { cache *thecache;
  176.  
  177.    thecache = cache_find(cacheaddr);
  178.    if (!thecache)
  179.     { cache *newcache;
  180.  
  181.       /**** If the cache is not active, add it to the     ****/
  182.       /**** active list and start the process immediately ****/
  183.       if ((newcache = malloc(sizeof(cache))) != NULL)
  184.        { newcache->cacheaddr = cacheaddr;
  185.          queue_empty(&newcache->pendingprocesses);
  186.          list_cons(&activecaches, (item *)newcache);
  187.          cache_cwfreport(cacheaddr,FALSE);
  188.          fn(cacheaddr, handle);
  189.          return(TRUE);
  190.        }
  191.     }
  192.    else
  193.     { pendingprocess *newprocess;
  194.  
  195.       /**** If the cache is active, add the process to the pending queue ****/
  196.       if ((newprocess = malloc(sizeof(pendingprocess))) != NULL)
  197.        { newprocess->fn = fn;
  198.          newprocess->handle = handle;
  199.  
  200.          queue_addvalue(&thecache->pendingprocesses, (item *)newprocess);
  201.          cache_cwfreport(cacheaddr,TRUE);
  202.          return(TRUE);
  203.        }
  204.     }
  205.    return(FALSE);
  206.  }
  207.  
  208.  
  209. void cache_finished(void *cacheaddr)
  210.  { cache *thecache;
  211.  
  212.    thecache = cache_find(cacheaddr);
  213.    assert(thecache != NULL);  /* cache_finish either called from an unregistered */
  214.                               /* process or called more than once from a         */
  215.                               /* registered process. */
  216.  
  217.    /**** If there are no pending processes then remove cache from ****/
  218.    /**** the active list otherwise remove a process and run it    ****/
  219.    if (queue_isempty(&thecache->pendingprocesses))
  220.     { list_remove(&activecaches, (item *)thecache);
  221.       free(thecache);
  222.       cache_cfreport(cacheaddr,FALSE);
  223.     }
  224.    else
  225.     { pendingprocess *process;
  226.  
  227.       process = (pendingprocess *)queue_removevalue(&thecache->pendingprocesses);
  228.       cache_cfreport(cacheaddr,TRUE);
  229.       process->fn(cacheaddr, process->handle);
  230.       free(process);
  231.     }
  232.  }
  233.  
  234. static BOOL match(item *iprocess, void *vpinfo)
  235.  { pendingprocess *process = (pendingprocess *)iprocess;
  236.    pendingprocess *pinfo   = vpinfo;
  237.  
  238.    return(process->fn == pinfo->fn && process->handle == pinfo->handle);
  239.  }
  240.  
  241. void cache_removeprocess(void *cacheaddr, cache_processstart fn, void *handle)
  242.  { cache *thecache;
  243.  
  244.    if ((thecache = cache_find(cacheaddr)) != NULL)
  245.     { item *process;
  246.       pendingprocess pinfo;
  247.  
  248.       pinfo.fn = fn;
  249.       pinfo.handle = handle;
  250.       /* A bit inefficient but won't make much difference unless */
  251.       /* you have a lot of processes queued up on the cache      */
  252.       while((process = queue_findandremovevalue(&thecache->pendingprocesses,
  253.                                                                match, &pinfo)) != NULL)
  254.        { free(process);
  255.        }
  256.     }
  257.    cache_crpreport(cacheaddr,fn,handle);
  258.  }
  259.  
  260.  
  261. static BOOL match2(item *iprocess, void *handle)
  262.  { pendingprocess *process = (pendingprocess *)iprocess;
  263.  
  264.    return(process->handle == handle);
  265.  }
  266.  
  267. void cache_removeallprocesses(void *cacheaddr, void *handle)
  268.  { cache *thecache;
  269.  
  270.    if ((thecache = cache_find(cacheaddr)) != NULL)
  271.     { item *process;
  272.  
  273.       while((process = queue_findandremovevalue(&thecache->pendingprocesses,
  274.                                                               match2, handle)) != NULL)
  275.        { free(process);
  276.        }
  277.     }
  278.    cache_crapreport(cacheaddr,handle);
  279.  }
  280.  
  281.  
  282. BOOL cache_inuse(void *cache)
  283.  { return(cache_find(cache) != NULL);
  284.  }
  285.  
  286. void *cache_address(euclid_header *structure)
  287.  { return(structure->cache ? structure->cache : structure);
  288.  }
  289.  
  290.  
  291.  
  292.  
  293. /***** Debugging routines *****/
  294. /* Loads of horrid messing about with files because !DDT seems to stop
  295.  * redirection of output. It also tended to leave the output file empty
  296.  * which is why I close it after every message and then reopen it.
  297.  */
  298.  
  299. #if CACHE_DEBUG
  300.  
  301. #include <stdio.h>
  302. #include "bbc.h"
  303.  
  304. static FILE *output = NULL;
  305.  
  306. static void cache_displayall(void)
  307.  { list caches;
  308.  
  309.    fputs(" Here are the active caches :", output);
  310.    fputc('\n',output);
  311.    for (caches = activecaches; !list_isempty(caches); caches=list_tail(caches))
  312.     { cache *thecache = (cache *)list_head(caches);
  313.       list processes;
  314.  
  315.       fprintf(output, " cacheaddr = %p   \n",thecache->cacheaddr);
  316.       fprintf(output, " pending   = ");
  317.       /* I now access the queue directly and treat its contents as a list */
  318.       /* This is VERY BAD programming practice, I'm only doing it because */
  319.       /* this is meant to be a debugging routine and I need to write it quickly */
  320.       for (processes = (list)thecache->pendingprocesses.front; !list_isempty(processes);
  321.            processes = list_tail(processes))
  322.        { pendingprocess *p;
  323.  
  324.          p = (pendingprocess *)list_head(processes);
  325.          fprintf(output, "%p(%p), ", p->fn, p->handle);
  326.        }
  327.       fputs("     ",output);
  328.       fputc('\n',output);
  329.     }
  330.    fputs("*****************",output);
  331.    fputc('\n',output);
  332.    if (output != stdout)
  333.     { fclose(output);
  334.     }
  335.  }
  336.  
  337. static void cache_reportoutput(void)
  338.  {/*output = fopen("<Example$Dir>.Cachedata","a");*/
  339.    output=stdout;
  340.    bbc_vdu(4); bbc_vdu(26);
  341.  }
  342.  
  343.  
  344. static void cache_cwfreport(void *cacheaddr, BOOL active)
  345.  { cache_reportoutput();
  346.    fprintf(output, "cache_callwhenfree(%p,...) - cache %sactive.    \n",
  347.                     cacheaddr,active?"":"not ");
  348.    cache_displayall();
  349.  }
  350.  
  351. static void cache_cfreport(void *cacheaddr, BOOL active)
  352.  { cache_reportoutput();
  353.    fprintf(output, "cache_finished(%p) - cache %s active.    \n",cacheaddr,
  354.                                                            active?"still":"no longer");
  355.    cache_displayall();
  356.  }
  357.  
  358. static void cache_crpreport(void *cacheaddr, cache_processstart fn, void *handle)
  359.  { cache_reportoutput();
  360.    fprintf(output, "cache_removeprocess(%p,%p,%p) \n", cacheaddr, fn, handle);
  361.    cache_displayall();
  362.  }
  363.  
  364. /* cache_removeallprocesses has an unfortunate acronym
  365.  *   - but at least I'm being consistant */
  366. static void cache_crapreport(void *cacheaddr, void *handle)
  367.  { cache_reportoutput();
  368.    fprintf(output, "cache_removeallprocesses(%p,%p) \n", cacheaddr, handle);
  369.    cache_displayall();
  370.  }
  371. #endif
  372.